#!/usr/bin/env python

from __future__ import division
import math
from collections import namedtuple

from matplotlib import pyplot

from grid import Grid
from interpolation import CubicSpline


def fxrange(start, end, step):
    x = start
    while x < end:
        yield x
        x += step
    yield end

def main():
    Function = namedtuple('Function', ('f', 'name', 'interval', 'nodes_number'))

    functions = (
            Function(f=lambda x: math.exp(-x), name='exp(-x)', interval=(0, 4), nodes_number=10),
            Function(f=math.log, name='ln(x)', interval=(1, 3), nodes_number=12),
            Function(f=math.sqrt, name='sqrt(x)', interval=(0, 4), nodes_number=12),
            Function(f=lambda x: 1 / x, name='1/x', interval=(1, 3), nodes_number=12),
            Function(f=math.sinh, name='sh(x)', interval=(0, 2), nodes_number=12),
            Function(f=math.atan, name='arctg(x)', interval=(0, 2), nodes_number=12),
            Function(f=math.cosh, name='ch(x)', interval=(0, 2), nodes_number=12),
            Function(f=math.tanh, name='th(x)', interval=(0, 2), nodes_number=12),
            Function(f=lambda x: 1/ math.sqrt(x), name='1/sqrt(x)', interval=(1, 3), nodes_number=12),
            Function(f=math.tan, name='tg(x)', interval=(0, 1.5), nodes_number=12)
            )

    print
    print '{0:10} {1:>5} {2:>10} {3:>15} {4:>10}'.format('name', 'nodes', 'true', 'interpolated', 'difference')
    for function in functions:
        a = function.interval[0]
        b = function.interval[1]
        mid_point = 0.7 * (b - a)
        xvalues = list(fxrange(a, b, (b - a) / function.nodes_number))
        yvalues = map(function.f, xvalues)
        g = Grid(xvalues, yvalues)
        cs = CubicSpline(g)

        true_value = function.f(mid_point)
        interpolated_value = cs.interpolate(mid_point)

        print '{0:10} {1:5} {2:10.4} {3:15.4} {4:10.4}'.format(function.name, function.nodes_number, true_value, interpolated_value, math.fabs(true_value - interpolated_value))
    print

    for function in functions:
        a = function.interval[0]
        b = function.interval[1]
        xvalues = list(fxrange(a, b, (b - a) / function.nodes_number))
        yvalues = map(function.f, xvalues)
        g = Grid(xvalues, yvalues)
        cs = CubicSpline(g)

        xvalues = list(fxrange(a, b, (b - a) / (function.nodes_number * 5)))
        true_yvalues = map(function.f, xvalues)
        spline_yvalues = map(cs.interpolate, xvalues)
        p1, p2 = pyplot.plot(xvalues, true_yvalues, xvalues, spline_yvalues)
        pyplot.legend((p1, p2), (function.name, 'interpolated {0}'.format(function.name)))
        pyplot.show()


if __name__ == '__main__':
    main()
